Repository navigation
SaveFileAsync: write hidden files again, check the SaveFilesAsync folder; test on Windows - #97
Merged
Merged
Conversation
…der; test on Windows
Fixes what the review of PR 84 found:
- SaveFileAsync opens with FileInfo.OpenWrite again and cuts the file to the
written length after the copy. FileMode.Create fails on an existing hidden
or system file on Windows, which 3.2.2 wrote. The trim is skipped for a
stream that cannot seek, such as /dev/null.
- SaveFilesAsync throws ArgumentException before downloading when the folder
is blank, an existing file, or looks like a result file name
(SaveFilesAsync("result.pdf")), instead of creating a folder named
result.pdf. An empty result list creates no folder, as in 3.2.2.
- The unit tests restore the static ApiToken and client they replace.
- build.yml runs the unit tests on Windows too, on net10.0 and net48, and
fails when no test runs. The build job waits for the Windows job and fails
if it failed, so build stays the one required check.
- CHANGELOG: the bug dates from 2.0.0 (2018); the exception changes are
recorded under Changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues block the file-saving and test-coverage changes.
No open issues 🎉
What changed in this PR
This PR refines the planned 3.3.0 file-saving fixes, restoring support for hidden and system files on Windows while preserving correct truncation when overwriting longer files.
Changes:
- Validate
SaveFilesAsyncdestinations before downloading, while preserving supported folder paths and empty-result behavior. - Expand file-saving regression tests and restore shared client and token state after each test.
- Add Windows testing for .NET 10 and .NET Framework 4.8, and configure CI to fail when no tests run.
| File | Description |
|---|---|
ConvertApi/ConvertApiExtension.cs |
Restore OpenWrite with post-copy truncation and validate output folders. |
ConvertApi.Tests/SaveFileTests.cs |
Add overwrite and folder-validation tests; restore static state after tests. |
ConvertApi.Tests/ConvertApi.Tests.csproj |
Add Windows-only .NET Framework 4.8 targeting and required imports. |
CHANGELOG.md |
Document file-saving behavior, compatibility changes, and expanded CI coverage. |
.github/workflows/build.yml |
Add Windows tests, gate the required build on their success, and reject zero-test runs. |
Review effort: Balanced
The tests run on Linux only again, in the one build job, which keeps TreatNoTestsAsError. build.yml and the test project match main apart from that flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of 3.3.0. This fixes what the review of the merged #84 found, before the change is released.
What changed
1. Hidden and system files on Windows (
ConvertApi/ConvertApiExtension.cs)FileMode.Create(CREATE_ALWAYS) throwsUnauthorizedAccessExceptionon an existing file with the Hidden or System attribute (UnauthorizedAccessException when writing to a file with Hidden attribute dotnet/runtime#117757). 3.2.2 wrote such files.SaveFileAsynctherefore opens the file withFileInfo.OpenWrite()again, as 3.2.2 did, and after the copy cuts it to the written length:if (fileStream.CanSeek && fileStream.Length > fileStream.Position) fileStream.SetLength(fileStream.Position);. That keeps PR 84's fix for corruption when saving over a longer file.CanSeekguard is there because an unguardedSetLengththrows on a device such as/dev/null.2. CI no longer passes with zero tests. The test step passes
-- RunConfiguration.TreatNoTestsAsError=true.3. No Windows CI. A
test (windows)job was tried here and passed, then dropped by decision: the tests run on Linux only, as before. Apart from the TreatNoTestsAsError flag,build.ymland the test project are unchanged.4. The tests no longer leak static state.
SaveFileTestsrestoresConvertApi.ApiTokenand the static client in[TearDown].5.
SaveFilesAsyncchecks its folder argument before downloading anything (option A).ArgumentException, namingSaveFileAsync, when the argument:SaveFilesAsync("result.pdf")for PDF results.mainwould quietly create a folder namedresult.pdf, which then blocksSaveFileAsync("result.pdf")."output","",".","v1.2","archive.pdf/"and an existing folder namedarchive.pdfall still work.6. CHANGELOG
UnauthorizedAccessException, which is not anIOException. 3.2.2 threwDirectoryNotFoundExceptionafter downloading.ArgumentException.The
0.0.0-localpack still shows NU5048 (PackageIconUrl). That warning was already onmainand is fixed in the 3.3.0 metadata PR.How it was tested
main),dotnet test ... -- RunConfiguration.TreatNoTestsAsError=true(17 of 17 pass), and pack.FileMode.Createfailed; CI runs it on Linux, where it always passes;/dev/null(Unix only);"result.pdf","RESULT.PDF"and"nested/result.pdf"are rejected with no folder created and no download;"archive.pdf/","v1.2"and"output.d"are accepted;archive.pdffolder is used;SaveFileAsyncover a 400,000-byte file left exactly the 146,734-byte PDF;SaveFilesAsyncinto a missing nested folder created it and saved the file;SaveFilesAsync(".../result.pdf")threwArgumentExceptionand created no folder;DeleteFilesAsyncremoved both server files.main, andmainis merged into this branch (CHANGELOG auto-merged): 23 of 23 tests pass.🤖 Generated with Claude Code